package com.zxj.netty.client;

import com.alibaba.fastjson.JSONObject;
import com.zxj.netty.annotation.SystemAlias;
import com.zxj.netty.model.NettyRequest;
import com.zxj.netty.model.NettyResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
 * 远程服务代理工厂
 */
public class ProxyFactory {

    /**
     * 对象属性的方法及属性类型的对应关系
     */
    private static final Map<Method, Class> map = new HashMap<>();

    /**
     * 客户端获取远程服务代理
     * @param type 远程服务接口类型
     * @return 远程服务代理
     */
    public static Enhancer getProxy(Class type) {
        // 封装Method-Class映射关系
        buildMethodClassMap(type);
        // 解析系统别名
        parseSystemAlias(type);

        Enhancer enhancer = new Enhancer();
        enhancer.setInterfaces(new Class[]{type});
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object bean, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                // 发送netty请求
                Object result = doInvoke(method, args[0]);
                return result;
            }
        });

        return enhancer;
    }

    public static Object getJdkProxy(Class type) {
        // 封装Method-Class映射关系
        buildMethodClassMap(type);
        // 解析系统别名
        parseSystemAlias(type);
        Object o = Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = doInvoke(method, args[0]);

                return result;
            }
        });

        return o;
    }

    private static Object doInvoke(Method method, Object arg) {
        // 发送netty请求
        NettyRequest request = new NettyRequest();
        request.setContent(arg);
        // 全类名.方法名
        request.setCmd(map.get(method).getName() + "." + method.getName());
        request.setSystemAlias(parseSystemAlias(map.get(method)));
        NettyResponse response = RpcClient.send(request);

        if (!StringUtils.equals(response.getCode(), "0")) {
            throw new RuntimeException(response.getText());
        }
        Class<?> returnType = method.getReturnType();
        return JSONObject.parseObject(JSONObject.toJSONString(response.getContent()), returnType);
    }

    private static Map<Method, Class> buildMethodClassMap(Class type) {
        Method[] declaredMethods = type.getDeclaredMethods();
        for (Method m : declaredMethods) {
            if (!map.containsKey(m)) {
                map.put(m, type);
                System.out.println("服务：" + type.getName() + "." + m.getName() + "订阅成功");
            }
        }
        return map;
    }

    private static String parseSystemAlias(Class<?> type) {
        SystemAlias annotation = type.getAnnotation(SystemAlias.class);
        ChannelManager.SYSTEMS.add(annotation.value());
        return annotation.value();
    }
}
